﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Data;
using System.Globalization;
using System.Text;
using System.Reflection;
using System.Collections;

namespace XNET.Extension
{
    /// <summary>
    /// IEnumerable扩展
    /// </summary>
    public static class IEnumerableExtensions
    {
        /// <summary>
        /// 链接多个集合，返回一个新的集合
        /// </summary>
        /// <typeparam name="T">集合的类型</typeparam>
        /// <param name="Enumerable1">集合1</param>
        /// <param name="Additions">多个集合参数组</param>
        /// <returns></returns>
        public static IEnumerable<T> Concat<T>(this IEnumerable<T> Enumerable1, params IEnumerable<T>[] Additions)
        {
            var Results = new List<T>();
            Results.AddRange(Enumerable1);
            for (var x = 0; x < Additions.Length; ++x)
            {
                Results.AddRange(Additions[x]);
            }
            return Results;
        }

        /// <summary>
        /// Returns only distinct items from the IEnumerable based on the predicate
        /// </summary>
        /// <typeparam name="T">Object type within the list</typeparam>
        /// <param name="Enumerable">List of objects</param>
        /// <param name="Predicate">
        /// Predicate that is used to determine if two objects are equal. True if they are the same,
        /// false otherwise
        /// </param>
        /// <returns>An IEnumerable of only the distinct items</returns>
        public static IEnumerable<T> Distinct<T>(this IEnumerable<T> Enumerable, Func<T, T, bool> Predicate)
        {
            var Results = new List<T>();
            foreach (T Item in Enumerable)
            {
                bool Found = false;
                foreach (T Item2 in Results)
                {
                    if (Predicate(Item, Item2))
                    {
                        Found = true;
                        break;
                    }
                }
                if (!Found)
                    Results.Add(Item);
            }
            return Results;
        }

        /// <summary>
        /// 返回集合中指定开始下标至结束下标元素
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Start">开始下标（包括）</param>
        /// <param name="End">结束下标</param>
        /// <returns></returns>
        public static IEnumerable<T> ElementsBetween<T>(this IEnumerable<T> List, int Start, int End)
        {
            if (List == null)
            {
                return List;
            }
            if (End > List.Count())
            {
                End = List.Count();
            }
            if (Start < 0)
            {
                Start = 0;
            }
            var ReturnList = new System.Collections.Generic.List<T>();
            for (var x = Start; x < End; ++x)
            {
                ReturnList.Add(List.ElementAt(x));
            }
            return ReturnList;
        }

        /// <summary>
        /// 为集合中指定开始下标至结束下标元素元素添加事件
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Start">开始元素</param>
        /// <param name="End">结束元素</param>
        /// <param name="Action">要执行的事件</param>
        /// <returns></returns>
        public static IEnumerable<T> For<T>(this IEnumerable<T> List, int Start, int End, Action<T> Action)
        {
            foreach (T Item in List.ElementsBetween(Start, End + 1))
            {
                Action(Item);
            }
            return List;
        }

        /// <summary>
        /// 为集合中指定开始下标至结束下标元素元素添加事件并返回结果
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">集合r</param>
        /// <param name="Start">开始元素</param>
        /// <param name="End">结束元素</param>
        /// <param name="Function">事件</param>
        /// <returns></returns>
        public static IEnumerable<R> For<T, R>(this IEnumerable<T> List, int Start, int End, Func<T, R> Function)
        {
            var ReturnValues = new List<R>();
            foreach (T Item in List.ElementsBetween(Start, End + 1))
            {
                ReturnValues.Add(Function(Item));
            }
            return ReturnValues;
        }

        /// <summary>
        /// 为集合中所有元素添加事件
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Action">事件</param>
        /// <returns></returns>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> List, Action<T> Action)
        {
            foreach (T Item in List)
            {
                Action(Item);
            }
            return List;
        }

        /// <summary>
        /// 为集合中所有元素添加事件并返回结果
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">结合</param>
        /// <param name="Function">事件</param>
        /// <returns></returns>
        public static IEnumerable<R> ForEach<T, R>(this IEnumerable<T> List, Func<T, R> Function)
        {
            var ReturnValues = new List<R>();
            foreach (T Item in List)
            {
                ReturnValues.Add(Function(Item));
            }
            return ReturnValues;
        }

        /// <summary>
        /// 为集合中所有元素添加事件(包含异常)
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Action">事件</param>
        /// <param name="CatchAction">事件异常</param>
        /// <returns>The original list</returns>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> List, Action<T> Action, Action<T, Exception> CatchAction)
        {
            foreach (T Item in List)
            {
                try
                {
                    Action(Item);
                }
                catch (Exception e)
                {
                    CatchAction(Item, e);
                }
            }
            return List;
        }

        /// <summary>
        /// 为集合中所有元素添加事件(包含异常)
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Function">事件</param>
        /// <param name="CatchAction">事件异常</param>
        /// <returns></returns>
        public static IEnumerable<R> ForEach<T, R>(this IEnumerable<T> List, Func<T, R> Function, Action<T, Exception> CatchAction)
        {
            var ReturnValues = new List<R>();
            foreach (T Item in List)
            {
                try
                {
                    ReturnValues.Add(Function(Item));
                }
                catch (Exception e)
                {
                    CatchAction(Item, e);
                }
            }
            return ReturnValues;
        }

        /// <summary>
        /// 为集合中指定开始下标至结束下标元素添加事件（并行）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Start">开始元素</param>
        /// <param name="End">结束元素</param>
        /// <param name="Action">事件</param>
        /// <returns></returns>
        public static IEnumerable<T> ForParallel<T>(this IEnumerable<T> List, int Start, int End, Action<T> Action)
        {
            Parallel.For(Start, End + 1, new Action<int>(x => Action(List.ElementAt(x))));
            return List;
        }

        /// <summary>
        /// 为集合中指定开始下标至结束下标元素添加事件带返回结果（并行）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Start">开始元素</param>
        /// <param name="End">结束元素</param>
        /// <param name="Function">事件</param>
        /// <returns></returns>
        public static IEnumerable<R> ForParallel<T, R>(this IEnumerable<T> List, int Start, int End, Func<T, R> Function)
        {
            var Results = new R[(End + 1) - Start];
            Parallel.For(Start, End + 1, new Action<int>(x => Results[x - Start] = Function(List.ElementAt(x))));
            return Results;
        }

        /// <summary>
        /// 为集合中所有元素添加事件（并行）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Action">事件</param>
        /// <returns></returns>
        public static IEnumerable<T> ForEachParallel<T>(this IEnumerable<T> List, Action<T> Action)
        {
            Parallel.ForEach(List, Action);
            return List;
        }

        /// <summary>
        /// 为集合中所有元素添加事件（并行）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Function">事件</param>
        /// <returns></returns>
        public static IEnumerable<R> ForEachParallel<T, R>(this IEnumerable<T> List, Func<T, R> Function)
        {
            return List.ForParallel(0, List.Count() - 1, Function);
        }

        /// <summary>
        /// 为集合中所有元素添加事件（并行包含事件异常）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Action">事件</param>
        /// <param name="CatchAction">异常</param>
        /// <returns></returns>
        public static IEnumerable<T> ForEachParallel<T>(this IEnumerable<T> List, Action<T> Action, Action<T, Exception> CatchAction)
        {
            Parallel.ForEach<T>(List, delegate(T Item)
            {
                try
                {
                    Action(Item);
                }
                catch (Exception e)
                {
                    CatchAction(Item, e);
                }
            });
            return List;
        }

        /// <summary>
        /// 为集合中所有元素添加事件并返回结果（并行包含事件异常）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <typeparam name="R">返回类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Function">事件</param>
        /// <param name="CatchAction">异常</param>
        /// <returns></returns>
        public static IEnumerable<R> ForEachParallel<T, R>(this IEnumerable<T> List, Func<T, R> Function, Action<T, Exception> CatchAction)
        {
            var ReturnValues = new List<R>();
            Parallel.ForEach<T>(List, delegate(T Item)
            {
                try
                {
                    ReturnValues.Add(Function(Item));
                }
                catch (Exception e)
                {
                    CatchAction(Item, e);
                }
            });
            return ReturnValues;
        }

        /// <summary>
        /// 返回结合最后X个元素
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Count">返回集合元素数量</param>
        /// <returns></returns>
        public static IEnumerable<T> Last<T>(this IEnumerable<T> List, int Count)
        {
            return List.ElementsBetween(List.Count() - Count, List.Count());
        }

        /// <summary>
        /// 从集合中删除指定值
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="Value">要删除项目</param>
        /// <param name="Predicate">删除项目事件</param>
        /// <returns></returns>
        public static IEnumerable<T> Remove<T>(this IEnumerable<T> Value, Func<T, bool> Predicate)
        {
            if (Value == null)
            {
                return Value;
            }
            return Value.Where(x => !Predicate(x));
        }

        /// <summary>
        /// 将集合转换为Array
        /// </summary>
        /// <typeparam name="Source">源类型</typeparam>
        /// <typeparam name="Target">目标类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="ConvertingFunction">为每个项添加事件</param>
        /// <returns></returns>
        public static Target[] ToArray<Source, Target>(this IEnumerable<Source> List, Func<Source, Target> ConvertingFunction)
        {
            return List.ForEach(ConvertingFunction).ToArray();
        }

        /// <summary>
        /// 将集合转换为DataTable
        /// </summary>
        /// <typeparam name="T">集合类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Columns">列名集合(如果为空，使用属性名称)</param>
        /// <returns>The list as a DataTable</returns>
        public static DataTable ToDataTable<T>(this IEnumerable<T> List, params string[] Columns)
        {
            var ReturnValue = new DataTable();
            ReturnValue.Locale = CultureInfo.CurrentCulture;
            if (List == null || List.Count() == 0)
            {
                return ReturnValue;
            }
            var Properties = typeof(T).GetProperties();
            if (Columns.Length == 0)
            {
                Columns = Properties.ToArray(x => x.Name);
            }
            Columns.ForEach(x => ReturnValue.Columns.Add(x, Properties.FirstOrDefault(z => z.Name == x).PropertyType));
            var Row = new object[Columns.Length];
            foreach (T Item in List)
            {
                for (var x = 0; x < Row.Length; ++x)
                {
                    Row[x] = Properties.FirstOrDefault(z => z.Name == Columns[x]).GetValue(Item, new object[] { });
                }
                ReturnValue.Rows.Add(Row);
            }
            return ReturnValue;
        }

        /// <summary>
        /// 把集合转换为LIST
        /// </summary>
        /// <typeparam name="Source">源类型</typeparam>
        /// <typeparam name="Target">目标类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="ConvertingFunction">转换事件</param>
        /// <returns></returns>
        public static List<Target> ToList<Source, Target>(this IEnumerable<Source> List, Func<Source, Target> ConvertingFunction)
        {
            return List.ForEach(ConvertingFunction).ToList();
        }

        /// <summary>
        /// 将集合转换字符，事件
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="List">集合转换</param>
        /// <param name="ItemOutput">用于将元素转换为字符串（默认调用toString）</param>
        /// <param name="Seperator">分隔符(默认,)</param>
        /// <returns></returns>
        public static string ToString<T>(this IEnumerable<T> List, Func<T, string> ItemOutput = null, string Seperator = ",")
        {
            Seperator = Seperator.Check(string.Empty);
            ItemOutput = ItemOutput.Check(x => x.ToString());
            var Builder = new StringBuilder();
            var TempSeperator = string.Empty;
            List.ForEach(x =>
            {
                Builder.Append(TempSeperator).Append(ItemOutput(x));
                TempSeperator = Seperator;
            });
            return Builder.ToString();
        }

        /// <summary>
        /// 如果所有项目符合条件，则抛出异常
        /// </summary>
        /// <typeparam name="T">项目类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Predicate">条件</param>
        /// <param name="Exception">指定异常</param>
        /// <returns>the original Item</returns>
        public static IEnumerable<T> ThrowIfAll<T>(this IEnumerable<T> List, Predicate<T> Predicate, Func<Exception> Exception)
        {
            foreach (T Item in List)
            {
                if (!Predicate(Item))
                {
                    return List;
                }
            }
            throw Exception();
        }

        /// <summary>
        /// 如果所有项目符合条件，则抛出异常
        /// </summary>
        /// <typeparam name="T">项目类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Predicate">条件</param>
        /// <param name="Exception">指定异常</param>
        /// <returns></returns>
        public static IEnumerable<T> ThrowIfAll<T>(this IEnumerable<T> List, Predicate<T> Predicate, Exception Exception)
        {
            foreach (T Item in List)
            {
                if (!Predicate(Item))
                {
                    return List;
                }
            }
            throw Exception;
        }

        /// <summary>
        /// Converts the IEnumerable to a DataTable
        /// </summary>
        /// <typeparam name="T">Type of the objects in the IEnumerable</typeparam>
        /// <param name="List">List to convert</param>
        /// <param name="Columns">Column names (if empty, uses property names)</param>
        /// <returns>The list as a DataTable</returns>
        public static DataTable To<T>(this IEnumerable<T> List, params string[] Columns)
        {
            var ReturnValue = new DataTable();
            ReturnValue.Locale = CultureInfo.CurrentCulture;
            if (List == null || List.Count() == 0)
                return ReturnValue;
            PropertyInfo[] Properties = typeof(T).GetProperties();
            if (Columns.Length == 0)
                Columns = Properties.ToArray(x => x.Name);
            Columns.ForEach(x => ReturnValue.Columns.Add(x, Properties.FirstOrDefault(z => z.Name == x).PropertyType));
            object[] Row = new object[Columns.Length];
            foreach (T Item in List)
            {
                for (int x = 0; x < Row.Length; ++x)
                {
                    Row[x] = Properties.FirstOrDefault(z => z.Name == Columns[x]).GetValue(Item, new object[] { });
                }
                ReturnValue.Rows.Add(Row);
            }
            return ReturnValue;
        }

        /// <summary>
        /// Converts the IEnumerable to a DataTable
        /// </summary>
        /// <param name="List">List to convert</param>
        /// <param name="Columns">Column names (if empty, uses property names)</param>
        /// <returns>The list as a DataTable</returns>
        public static DataTable To(this IEnumerable List, params string[] Columns)
        {
            var ReturnValue = new DataTable();
            ReturnValue.Locale = CultureInfo.CurrentCulture;
            int Count = 0;
            var i = List.GetEnumerator();
            while (i.MoveNext())
                ++Count;
            if (List == null || Count == 0)
                return ReturnValue;
            IEnumerator ListEnumerator = List.GetEnumerator();
            ListEnumerator.MoveNext();
            PropertyInfo[] Properties = ListEnumerator.Current.GetType().GetProperties();
            if (Columns.Length == 0)
                Columns = Properties.ToArray(x => x.Name);
            Columns.ForEach(x => ReturnValue.Columns.Add(x, Properties.FirstOrDefault(z => z.Name == x).PropertyType));
            object[] Row = new object[Columns.Length];
            foreach (object Item in List)
            {
                for (int x = 0; x < Row.Length; ++x)
                {
                    Row[x] = Properties.FirstOrDefault(z => z.Name == Columns[x]).GetValue(Item, new object[] { });
                }
                ReturnValue.Rows.Add(Row);
            }
            return ReturnValue;
        }

        /// <summary>
        /// 如果任意项目符合条件，则抛出异常
        /// </summary>
        /// <typeparam name="T">项目类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Predicate">条件</param>
        /// <param name="Exception">指定异常</param>
        /// <returns></returns>
        public static IEnumerable<T> ThrowIfAny<T>(this IEnumerable<T> List, Predicate<T> Predicate, Func<Exception> Exception)
        {
            foreach (T Item in List)
            {
                if (Predicate(Item))
                {
                    throw Exception();
                }
            }
            return List;
        }

        /// <summary>
        /// 如果任意项目符合条件，则抛出异常
        /// </summary>
        /// <typeparam name="T">项目类型</typeparam>
        /// <param name="List">集合</param>
        /// <param name="Predicate">条件</param>
        /// <param name="Exception">指定异常</param>
        /// <returns></returns>
        public static IEnumerable<T> ThrowIfAny<T>(this IEnumerable<T> List, Predicate<T> Predicate, Exception Exception)
        {
            foreach (T Item in List)
            {
                if (Predicate(Item))
                {
                    throw Exception;
                }
            }
            return List;
        }

        /// <summary>
        /// Determines if the IEnumerable is not null or empty and throws an ArgumentException if it is
        /// </summary>
        /// <typeparam name="T">Item type</typeparam>
        /// <param name="Item">The object to check</param>
        /// <param name="Name">Name of the argument</param>
        /// <returns>Returns Item</returns>
        public static IEnumerable<T> ThrowIfNotNullOrEmpty<T>(this IEnumerable<T> Item, string Name)
        {
            return Item.ThrowIfNotNullOrEmpty(new ArgumentException(Name));
        }

        /// <summary>
        /// Determines if the IEnumerable is not null or empty and throws the exception passed in if it is
        /// </summary>
        /// <typeparam name="T">Item type</typeparam>
        /// <param name="Item">The object to check</param>
        /// <param name="Exception">Exception to throw</param>
        /// <returns>Returns Item</returns>
        public static IEnumerable<T> ThrowIfNotNullOrEmpty<T>(this IEnumerable<T> Item, Exception Exception)
        {
            return Item.ThrowIf(x => x != null && x.Count() > 0, Exception);
        }

        /// <summary>
        /// Determines if the IEnumerable is null or empty and throws an ArgumentNullException if it is
        /// </summary>
        /// <typeparam name="T">Item type</typeparam>
        /// <param name="Item">The object to check</param>
        /// <param name="Name">Name of the argument</param>
        /// <returns>Returns Item</returns>
        public static IEnumerable<T> ThrowIfNullOrEmpty<T>(this IEnumerable<T> Item, string Name)
        {
            return Item.ThrowIfNullOrEmpty(new ArgumentNullException(Name));
        }

        /// <summary>
        /// Determines if the IEnumerable is null or empty and throws the exception passed in if it is
        /// </summary>
        /// <typeparam name="T">Item type</typeparam>
        /// <param name="Item">The object to check</param>
        /// <param name="Exception">Exception to throw</param>
        /// <returns>Returns Item</returns>
        public static IEnumerable<T> ThrowIfNullOrEmpty<T>(this IEnumerable<T> Item, Exception Exception)
        {
            return Item.ThrowIf(x => x == null || x.Count() == 0, Exception);
        }

    }
}
